package com.googlecode.dgwt.client.dijit.view.model;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.event.shared.HandlerRegistration;
import com.googlecode.dgwt.client.dijit.Dijit;
import com.googlecode.dgwt.client.dijit.View;
import com.googlecode.dgwt.client.dijit.event.RowDeselectEvent;
import com.googlecode.dgwt.client.dijit.event.RowDeselectHandler;
import com.googlecode.dgwt.client.dijit.event.RowEvent;
import com.googlecode.dgwt.client.dijit.event.RowSelectEvent;
import com.googlecode.dgwt.client.dijit.event.RowSelectHandler;
import com.googlecode.dgwt.client.dijit.view.store.CellView;
import com.googlecode.dgwt.client.dijit.view.store.RowView;
import com.googlecode.dgwt.client.dojo.Dojo;
import com.googlecode.dgwt.client.dojo.data.model.StoreColumnBase;
import com.googlecode.dgwt.client.dojo.util.NativeList;
import com.googlecode.dgwt.client.dojox.data.model.DataColumn;
import com.googlecode.dgwt.client.dojox.data.model.ProxyColumn;

public abstract class ColumnSelector<Item> extends ProxyColumn<Item,Object> implements RowSelectHandler, RowDeselectHandler {

    private Map<View,List<HandlerRegistration>> eventMap = new HashMap<View, List<HandlerRegistration>>();
    
    public ColumnSelector() {
        super(null);
    }

    public ColumnSelector(String title) {
        this(title, null);
    }

    public ColumnSelector(String title, String width) {
        super(new DataColumn<Item,Object>(Object.class, title, width){
            
            @Override
            protected Object onGetValue(Item item) {
                return null;
            }

            @Override
            protected void onSetValue(Item item, Object value) {
            }
            
        });
    }

    @SuppressWarnings("unchecked")
    public ColumnSelector(StoreColumnBase<Item,?> column) {
        super((StoreColumnBase<Item, Object>) column);
    }

    protected abstract Node getCellContent(Element cellElement);
    
    protected abstract Node generateCellContent(Element cellElement);
    
    protected abstract void setCellChecked(Node content, boolean checked);
    
    protected abstract void setCellText(Node content, String displayValue);
    
    
    @Override
    public void onUpdateColumn(View view, Element cellElement, Item item) {
        Node content = generateOrGetContent(cellElement);
        setCellChecked(content, isCellSelected(view, cellElement));
        
        String displayValue = getDisplayValue(item);
        setCellText(content, displayValue == null ? "" : displayValue);
    }

    protected Node generateOrGetContent(Element cellElement) {
        Node content = getCellContent(cellElement);  
        if (content == null){
            content = generateCellContent(cellElement);
            Dojo.addClass(cellElement, Dijit.getTheme().dijitSelectorName());
        }
        return content;
    }

    protected List<HandlerRegistration> getEventList(View parentView){
        List<HandlerRegistration> eventList = eventMap.get(parentView);
        if (eventList == null){
            eventList = new NativeList<HandlerRegistration>();
            eventMap.put(parentView, eventList);
        }
        return eventList;
    }
    
    protected boolean isCellSelected(View view, Element cellElement){
        if (view instanceof RowView){
            RowView rowView = (RowView) view;
            return rowView.isRowSelected(rowView.getRowIndex(cellElement));
        }
        return false;
    }
    
    protected Element getCellElement(RowEvent<?> event){
        if (event.getSource() instanceof RowView){
            RowView hasRows = (RowView) event.getSource();
            if (hasRows instanceof CellView){
                return ((CellView) hasRows).getCell(event.getRowIndex(), getColumnIndex());
            }
            return hasRows.getRow(event.getRowIndex());
        }
        return null;
    }

    @Override
    public void onRegisterParent(View parentView, boolean registered) {
        super.onRegisterParent(parentView, registered);
        
        if (parentView instanceof RowView){
            RowView hasRows = (RowView) parentView;
            
            if (registered){
                List<HandlerRegistration> eventList = getEventList(parentView);
                eventList.add(hasRows.addRowSelectHandler(this));
                eventList.add(hasRows.addRowDeselectHandler(this));
            } else {
                for (Iterator<HandlerRegistration> iterator = getEventList(parentView).iterator(); iterator.hasNext();){
                    iterator.next().removeHandler();
                    iterator.remove();
                }
            }
        }
    }

    @Override
    public void onRowSelect(RowSelectEvent event) {
        Element cellElement = getCellElement(event);
        if (cellElement != null){
            setCellChecked(generateOrGetContent(cellElement), true);
        }
    }

    @Override
    public void onRowDeselect(RowDeselectEvent event) {
        Element cellElement = getCellElement(event);
        if (cellElement != null){
            setCellChecked(generateOrGetContent(cellElement), false);
        }
    }

}
